package com.loong.android.viewpager;

import android.os.Bundle;
import android.os.Parcelable;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Lifecycle;

import com.loong.android.LonFragment;

import java.util.ArrayList;
import java.util.List;

public class PagerFragmentAdapter<T> extends PagerBaseAdapter<T> {
    private final static String TAG = PagerFragmentAdapter.class.getSimpleName();

    /**
     * 显示的Fragment变化时的行为类型表
     */
    public static class BehaviorType {
        /**
         * 当正在显示的Fragment变化时，触发{@link LonFragment#onFragmentVisibleChange(boolean)}事件
         */
        public final static int SET_USER_VISIBLE_HINT = 0;
        /**
         * 当正在显示的Fragment变化时，仅正在显示的Fragment会触发{@link LonFragment#onFragmentResume()}事件
         */
        public final static int RESUME_ONLY_CURRENT_FRAGMENT = 1;
    }

    private final androidx.fragment.app.FragmentManager mFragmentManager;
    private final int mBehavior;
    private FragmentTransaction mCurTransaction = null;

    private List<ItemInfo> mItemInfoList = new ArrayList<>();
    private final ArrayList<Fragment.SavedState> mSavedState = new ArrayList<>();
    private Fragment mCurrentPrimaryItem = null;
    private boolean mExecutingFinishUpdate;
    private boolean mNeedSortItemInfoList = false;

    /**
     * 构造函数
     * @param fm 界面碎片管理器对象
     */
    public PagerFragmentAdapter(FragmentManager fm) {
        this(fm, BehaviorType.SET_USER_VISIBLE_HINT);
    }

    /**
     * 构造函数
     * @param fm 界面碎片管理器对象，可以通过{@link com.loong.android.LonWin#getFragmentManager()}方法获取
     * @param behavior 行为类型，参考{@link BehaviorType#SET_USER_VISIBLE_HINT} 和 {@link BehaviorType#RESUME_ONLY_CURRENT_FRAGMENT}
     */
    public PagerFragmentAdapter(FragmentManager fm, int behavior) {
        mFragmentManager = fm;
        mBehavior = behavior;
    }

    /**
     * 获取行为类型
     */
    public int getBehavior() {
        return mBehavior;
    }

    @Override
    protected void onStartUpdate(ViewGroup container) {
        if (container.getId() == View.NO_ID) {
            throw new IllegalStateException("ViewPager with adapter " + this + " requires a view id");
        }
    }

    @Override
    protected Object onInstantiateItem(ViewGroup container, int position) {
        if (mItemInfoList.size() > position) {
            ItemInfo itemInfo = mItemInfoList.get(position);
            if (itemInfo != null) return itemInfo;
        }

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        Fragment fragment = (Fragment) getItem(position);

        if (mSavedState.size() > position) {
            Fragment.SavedState fss = mSavedState.get(position);
            if (fss != null) {
                fragment.setInitialSavedState(fss);
            }
        }
        while (mItemInfoList.size() <= position) {
            mItemInfoList.add(null);
        }
        fragment.setMenuVisibility(false);
        if (mBehavior == BehaviorType.SET_USER_VISIBLE_HINT) {
            fragment.setUserVisibleHint(false);
        }
        ItemInfo itemInfo = new ItemInfo(fragment, position);
        mItemInfoList.set(position, itemInfo);
        mCurTransaction.add(container.getId(), fragment);

        if (mBehavior == BehaviorType.RESUME_ONLY_CURRENT_FRAGMENT) {
            mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
        }
        return itemInfo;
    }

    @Override
    protected int onGetItemPosition(Object object) {
        ItemInfo itemInfo = (ItemInfo) object;
        Fragment fragment = itemInfo.fragment;
        int oldPos = mItemInfoList.indexOf(object);
        if(oldPos >= 0 && getItem(oldPos) != fragment) {
            int count = getCount();
            int newPos = POSITION_NONE;
            for (int i = 0; i < count; i++) {
                if (getItem(i) != fragment) continue;
                newPos = i;
                break;
            }
            itemInfo.position = newPos;
            mNeedSortItemInfoList = true;
            return newPos;
        }
        return POSITION_UNCHANGED;
    }

    @Override
    protected void onDestroyItem(ViewGroup container, int position, Object object) {
        ItemInfo itemInfo = (ItemInfo) object;
        Fragment fragment = itemInfo.fragment;

        while (mSavedState.size() <= position) {
            mSavedState.add(null);
        }
        mSavedState.set(position, fragment.isAdded() ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
        mItemInfoList.set(position, null);
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        mCurTransaction.remove(fragment);

        if (fragment.equals(mCurrentPrimaryItem)) {
            mCurrentPrimaryItem = null;
        }
    }

    private void remakeItemInfoList() {
        if(!mNeedSortItemInfoList) return;
        mNeedSortItemInfoList = false;
        List<ItemInfo> infoList = new ArrayList<>();
        for (ItemInfo itemInfo : mItemInfoList) {
            if(itemInfo == null) continue;
            if(itemInfo.position < 0) continue;
            while (infoList.size() <= itemInfo.position) {
                infoList.add(null);
            }
            infoList.set(itemInfo.position, itemInfo);
        }
        mItemInfoList = infoList;
    }

    @Override
    protected void onSetPrimaryItem(ViewGroup container, int position, Object object) {
        Fragment fragment = ((ItemInfo) object).fragment;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
                if (mBehavior == BehaviorType.RESUME_ONLY_CURRENT_FRAGMENT) {
                    if (mCurTransaction == null) {
                        mCurTransaction = mFragmentManager.beginTransaction();
                    }
                    mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
                } else {
                    mCurrentPrimaryItem.setUserVisibleHint(false);
                }
            }
            fragment.setMenuVisibility(true);
            if (mBehavior == BehaviorType.RESUME_ONLY_CURRENT_FRAGMENT) {
                if (mCurTransaction == null) {
                    mCurTransaction = mFragmentManager.beginTransaction();
                }
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
            } else {
                fragment.setUserVisibleHint(true);
            }

            mCurrentPrimaryItem = fragment;
        }
    }

    @Override
    protected void onFinishUpdate(ViewGroup container) {
        remakeItemInfoList();
        if (mCurTransaction != null) {
            if (!mExecutingFinishUpdate) {
                try {
                    mExecutingFinishUpdate = true;
                    mCurTransaction.commitNowAllowingStateLoss();
                } finally {
                    mExecutingFinishUpdate = false;
                }
            }
            mCurTransaction = null;
        }
    }

    @Override
    protected boolean checkIsViewFromObject(Object view, Object object) {
        return ((ItemInfo) object).fragment.getView() == view;
    }

    @Nullable
    @Override
    public Parcelable saveState() {
        Bundle state = null;
        if (mSavedState.size() > 0) {
            state = new Bundle();
            Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
            mSavedState.toArray(fss);
            state.putParcelableArray("states", fss);
        }
        for (int i = 0; i< mItemInfoList.size(); i++) {
            Fragment f = mItemInfoList.get(i).fragment;
            if (f != null && f.isAdded()) {
                if (state == null) {
                    state = new Bundle();
                }
                String key = "f" + i;
                mFragmentManager.putFragment(state, key, f);
            }
        }
        return null;
    }

    @Override
    public void restoreState(@Nullable Parcelable state, @Nullable ClassLoader loader) {
        if (state != null) {
            Bundle bundle = (Bundle)state;
            bundle.setClassLoader(loader);
            Parcelable[] fss = bundle.getParcelableArray("states");
            mSavedState.clear();
            mItemInfoList.clear();
            if (fss != null) {
                for (int i=0; i<fss.length; i++) {
                    mSavedState.add((Fragment.SavedState)fss[i]);
                }
            }
            Iterable<String> keys = bundle.keySet();
            for (String key: keys) {
                if (key.startsWith("f")) {
                    int index = Integer.parseInt(key.substring(1));
                    Fragment f = mFragmentManager.getFragment(bundle, key);
                    if (f != null) {
                        while (mItemInfoList.size() <= index) {
                            mItemInfoList.add(null);
                        }
                        f.setMenuVisibility(false);
                        mItemInfoList.set(index, new ItemInfo(f, index));
                    }
                }
            }
        }
    }

    public static class ItemInfo {
        public int position = -1;
        public Fragment fragment = null;
        public ItemInfo(Fragment fragment, int position) {
            this.position = position;
            this.fragment = fragment;
        }
    }
}
